package com.motu.monstercity.server.game.timedtask;

import com.motu.monstercity.module.common.redis.RedisKey;
import com.motu.monstercity.module.common.utility.EventBusConstant;
import com.motu.monstercity.module.common.utility.ServerParam;
import com.motu.vertx.module.utility.base.ServerConfig;
import com.motu.vertx.module.utility.main.MainService;
import com.motu.vertx.module.utility.mysql.MysqlClient;
import com.motu.vertx.module.utility.mysql.MysqlTool;
import com.motu.vertx.module.utility.redis.RedisTools;
import com.motu.vertx.module.utility.toolset.TimeUtils;
import com.motu.vertx.module.utility.toolset.Tool;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

public class TimedTaskManager {
	public final static Logger logger = LoggerFactory.getLogger(TimedTaskManager.class);


	/**
	 * 初始化定时任务
	 */
	public static void init() {
		// 定时任务初始化只能由定时任务主进程执行
		if (!ServerConfig.isTimedTaskServer() || ServerConfig.isTimedTaskWorker()) {
			return;
		}

		logger.info("TimedTaskManager.init !!!!!!");

		// 初始化延迟队列信息
		DelayQueueManager.init();

		// 记录协议请求并发信息
		if (!ServerConfig.isLocalServer()) {
			// 定时服重启清空协议并发请求数量
			if (ServerParam.PROTOCOL_REQUEST_COUNT_TYPE > 0) {
				logger.info("protocol request reset");
				RedisTools.del(RedisKey.getProtocolRequestKey());
			}

			// 每隔1分钟记录一次协议请求并发数量
			MainService.getVertx().setPeriodic(60000L, handler -> {
				recordProtocolRequestNum();
			});
		}
	}

	/**
	 * 初始化活动信息（后台重载活动会调用到）
	 */
	public static void initAct() {
		DelayQueueManager.initAct();
	}

	/**
	 * 给定时任务主线程发送消息
	 *
	 * @param cmd  命令
	 * @param data 推送消息
	 */
	public static void sendMainTask(int cmd, JsonObject data) {
		JsonObject message = new JsonObject();
		message.put("cmd", cmd);
		message.put("data", data);
		logger.info("sendMainTask message = " + message.toString());
		String address = EventBusConstant.getAddress(EventBusConstant.TIME_MAIN);
		EventBus eventBus = MainService.getVertx().eventBus();
		eventBus.send(address, message);
	}

	/**
	 * 给定时任务主线程发送消息，发送失败需要重复,接收代码必须有message.reply(new JsonObject()); 不然会超时
	 * @param cmd
	 * @param data
	 */
	public static void sendMainTaskRetry(int cmd, JsonObject data) {
		JsonObject message = new JsonObject();
		message.put("cmd", cmd);
		message.put("data", data);
		logger.info("sendMainTaskRetry message = " + message.toString());
		String address = EventBusConstant.getAddress(EventBusConstant.TIME_MAIN);
		EventBus eventBus = MainService.getVertx().eventBus();
		eventBus.request(address, message, h -> {
			if (h.failed()) {// 发送失败需要重发
				logger.error("sendMainTaskRetry send error message = " + message.toString() + " cause = " + Tool.getException(h.cause()));
				sendMainTask(cmd, data);
			}
		});
	}

	/**
	 * 执行一些并行任务,只管发送,不需要知道是否有接收到
	 *
	 * @param cmd  命令
	 * @param data 推送消息
	 */
	public static void sendWorkTask(int cmd, JsonObject data) {
		JsonObject message = new JsonObject();
		message.put("cmd", cmd);
		message.put("data", data);
		logger.info("sendWorkTask message = " + message.toString());
		String address = EventBusConstant.getAddress(EventBusConstant.TIME_WORK);
		EventBus eventBus = MainService.getVertx().eventBus();
		eventBus.send(address, message);
	}

	/**
	 * 执行一些并行任务,比较重要的cmd,发送失败需要重复,接收代码必须有message.reply(new JsonObject()); 不然会超时
	 * @param cmd
	 * @param data
	 */
	public static void sendWorkTaskRetry(int cmd, JsonObject data) {
		JsonObject message = new JsonObject();
		message.put("cmd", cmd);
		message.put("data", data);
		logger.info("sendWorkTaskRetry message = " + message.toString());
		String address = EventBusConstant.getAddress(EventBusConstant.TIME_WORK);
		EventBus eventBus = MainService.getVertx().eventBus();
		eventBus.request(address, message, h -> {
			if (h.failed()) {// 发送失败需要重发
				logger.error("sendWorkTaskRetry send error message = " + message.toString() + " cause = " + Tool.getException(h.cause()));
				sendWorkTask(cmd, data);
			}
		});
	}


	/**
	 * 执行一些并行任务
	 *
	 * @param cmd  命令
	 * @param data 推送消息
	 */
	public static Future<Void> sendWorkTaskFuture(int cmd, JsonObject data) {
		Promise<Void> promise = Promise.promise();
		JsonObject message = new JsonObject();
		message.put("cmd", cmd);
		message.put("data", data);
		String address = EventBusConstant.getAddress(EventBusConstant.TIME_WORK);
		EventBus eventBus = MainService.getVertx().eventBus();
		eventBus.request(address, message, h -> {
			if (h.succeeded()) {
				logger.info("sendWorkTaskFuture success !!!");
				promise.complete();
			} else {
				logger.error("sendWorkTaskFuture fail !!!");
				promise.fail(Tool.getException(h.cause()));
			}
		});
		return promise.future();
	}

	/**
	 * 记录协议请求并发数量
	 */
	private static void recordProtocolRequestNum() {
		if (ServerParam.PROTOCOL_REQUEST_COUNT_TYPE == 0) {
			return;
		}
		String key = RedisKey.getProtocolRequestKey();
		RedisTools.getHashJsonObject(key, h -> {
			if (h.succeeded()) {
				JsonObject result = h.result();
				if (result != null && !result.isEmpty()) {
					long curTime = TimeUtils.getCurTime();
					List<JsonArray> paramlist = new ArrayList<>();
					for (Iterator<Entry<String, Object>> iter = result.iterator(); iter.hasNext(); ) {
						Entry<String, Object> entry = iter.next();
						int cmdCode = Integer.parseInt(entry.getKey());
						int num = Integer.parseInt(entry.getValue().toString());
						if (num > 1) {
							JsonArray param = new JsonArray();
							param.add(cmdCode);
							param.add(num);
							param.add(curTime);
							paramlist.add(param);
						}
					}
					if (paramlist.size() > 0) {
						String sql = "insert into monitor_request(cmd_code, num, add_time) values(?, ?, ?);";
						MysqlTool.batchUpdateToDb(MysqlClient.getServerPool(), sql, paramlist, handler -> {
							if (handler.succeeded()) {
								logger.info("protocol request insert monitor_request count = {} time = {}", paramlist.size(), curTime);
							}
						});
					}
				}
			}
		});
	}

}
